package flexibilityDesign;

import ilog.concert.IloLinearNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;

import java.math.BigDecimal;

import util.NullOutputStream;
import concurrent.Job;

public class DualSolverCapInvest extends Job<DualResult> {
	private double[] demand;
	private int product_num;
	private int plant_num;
	private double[] capacities;
	private double[][] profit_m;
	private int[][] YValue;

	private double[][] corePoint = null;

	private int taskIndex = 0;

	public DualSolverCapInvest(int n, int m, double[] demand, double[][] profit, int[][] YValue, double[] capacities,
			int taskIndex) {
		this.plant_num = m;// plant num
		this.product_num = n;// product num
		this.demand = demand;
		this.profit_m = profit;
		this.capacities = capacities;
		this.taskIndex = taskIndex;
		this.YValue = YValue;
	}

	public DualSolverCapInvest(int n, int m, double[] demand, int[][] Yvalue, double[][] corePoint, double[][] profit,
			double[] capacities, int taskIndex) {
		this.plant_num = m;
		this.product_num = n;
		this.demand = demand;
		this.profit_m = profit;
		this.capacities = capacities;
		this.taskIndex = taskIndex;
		this.corePoint = corePoint;
	}

	@Override
	public DualResult execute() {
		DualResult result = new DualResult();
		try {
			IloCplex dual = new IloCplex();
			dual.setOut(new NullOutputStream());
			IloNumVar[] phi = dual.numVarArray(plant_num, Double.NEGATIVE_INFINITY, 0.0);
			IloNumVar[] sigma = dual.numVarArray(product_num, Double.NEGATIVE_INFINITY, 0.0);
			IloNumVar[][] gamma = new IloNumVar[product_num][plant_num];
			for (int i = 0; i < product_num; i++) {
				for (int j = 0; j < plant_num; j++)
					gamma[i][j] = dual.numVar(Double.NEGATIVE_INFINITY, 0.0);
			}

			double[] phiVal = new double[plant_num];
			double[] sigmaVal = new double[product_num];
			double[][] gammaVal = new double[product_num][plant_num];
			double dualObjVal = 0.0;

			IloLinearNumExpr dualObj = dual.linearNumExpr();
			for (int j = 0; j < plant_num; j++)
				dualObj.addTerm(capacities[j], phi[j]);
			for (int i = 0; i < product_num; i++)
				dualObj.addTerm(demand[i], sigma[i]);
			for (int i = 0; i < product_num; i++) {
				for (int j = 0; j < plant_num; j++) {
					dualObj.addTerm(Support.DEMAND_UP * YValue[i][j], gamma[i][j]);
					// constraint
					dual.addLe(dual.sum(gamma[i][j], phi[j], sigma[i]), -profit_m[i][j]);
				}
			}
			dual.addMaximize(dualObj);

			if (dual.solve()) {
				dualObjVal = dual.getObjValue();

				sigmaVal = dual.getValues(sigma);
				phiVal = dual.getValues(phi);

				for (int i = 0; i < product_num; i++)
					for (int j = 0; j < plant_num; j++)
						gammaVal[i][j] = dual.getValue(gamma[i][j]);
			}

			BigDecimal et = BigDecimal.ZERO;
			BigDecimal[][] Et = new BigDecimal[product_num + 1][plant_num];
			for (int i = 0; i < product_num; i++)
				for (int j = 0; j < plant_num; j++)
					Et[i][j] = BigDecimal.valueOf(-Support.DEMAND_UP * gammaVal[i][j]);
			for (int j = 0; j < plant_num; j++)
				Et[product_num][j] = BigDecimal.valueOf(-phiVal[j]);

			for (int i = 0; i < product_num; i++) {
				et = et.add(BigDecimal.valueOf(sigmaVal[i] * demand[i]));
			}

			result.setEt(Et);
			result.setEtm(et);
			result.setObjValue(dualObjVal);
			result.setTaskIndex(taskIndex);

			dual.end();

		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

}
